﻿using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ThePresenter.Net.PresenterCom.Uids;
using ThePresenter.Windows;


namespace ThePresenter
{
	public class Slide : ISerializable, INotifyPropertyChanged, ICloneable
	{
		#region Fields
		/// <summary>
		/// The canvas.
		/// </summary>
		InkCanvas _Canvas;

		/// <summary>
		/// The size.
		/// </summary>
		Size _Size;

		/// <summary>
		/// The thumbnail.
		/// </summary>
		ImageSource _Thumbnail;

		/// <summary>
		/// The tag.
		/// </summary>
		object _Tag;

		/// <summary>
		/// The serializable Guid of slide.
		/// </summary>
		Uid _Uid;
		#endregion


		#region Properties
		/// <summary>
		/// Gets or sets the canvas.
		/// </summary>
		/// <value>The canvas.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public InkCanvas Canvas
		{
			get
			{
				return _Canvas;
			}
			set
			{
				if (_Canvas != value)
				{
					if (_Canvas != null) // detach event handlers from old canvas
					{
						DetachCanvasHandlers(_Canvas);
					}

					_Canvas = value;
					OnPropertyChanged("Canvas");

					if (_Canvas != null) // attach event handlers to new canvas
					{
						AttachCanvasHandlers(_Canvas);

						if (double.IsNaN(_Canvas.Width))
							_Canvas.Width = _Size.Width;

						if (double.IsNaN(_Canvas.Height))
							_Canvas.Height = _Size.Height;

						_Size = new Size(_Canvas.Width, _Canvas.Height);
					}
				}
			}
		}

		/// <summary>
		/// Gets or sets the background.
		/// </summary>
		/// <value>The background.</value>
		public string Background
		{
			get
			{
				if (_Canvas != null)
				{
					if (_Canvas.Background is ImageBrush)
					{
						// ByteArray in XML generates big overhead, so serialize to Base64 string
						return Convert.ToBase64String((((_Canvas.Background as ImageBrush).ImageSource as BitmapImage).StreamSource as MemoryStream).ToArray());
					}
					else if (_Canvas.Background is SolidColorBrush)
					{
						return (_Canvas.Background as SolidColorBrush).ToString(); // hex string
					}
				}

				return null;
			}
			set
			{
				if (!string.IsNullOrEmpty(value))
				{
					if (Canvas == null)
						Canvas = new InkCanvas();

					if (value.StartsWith("#")) // hex string - solid color
					{
						Canvas.Background = new SolidColorBrush(Helpers.GetColorFromHexString(value));
					}
					else // image
					{
						var ms = new MemoryStream(Convert.FromBase64String(value));

						Canvas.Background = new ImageBrush(Helpers.GetBitmapImageFromStream(ms));
					}

					OnPropertyChanged("Background");
				}
			}
		}

		/// <summary>
		/// Gets or sets the strokes.
		/// </summary>
		/// <value>The strokes.</value>
		public string Strokes
		{
			get
			{
				if (Canvas != null && Canvas.Strokes != null)
				{
					var conv = new System.Windows.StrokeCollectionConverter();

					return conv.ConvertToString(Canvas.Strokes);
				}
				else
				{
					return null;
				}
			}
			set
			{
				if (value != null)
				{
					if (Canvas == null)
						Canvas = new InkCanvas();

					var conv = new System.Windows.StrokeCollectionConverter();

					Canvas.Strokes = (StrokeCollection)conv.ConvertFromString(value);
				}
			}
		}

		/// <summary>
		/// Gets or sets the the size.
		/// </summary>
		/// <value>The size.</value>
		public Size Size
		{
			get
			{
				return _Size;
			}
			set
			{
				if (_Size != value)
				{
					_Size = value;
					OnPropertyChanged("Size");

					// HACK Size property
					if (_Canvas != null)
					{
						_Canvas.Width = value.Width;
						_Canvas.Height = value.Height;
					}
				}
			}
		}

		/// <summary>
		/// Gets or sets the thumbnail.
		/// </summary>
		/// <value>The thumbnail.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public ImageSource Thumbnail
		{
			get
			{
				return _Thumbnail;
			}
			protected set
			{
				_Thumbnail = value;
				OnPropertyChanged("Thumbnail");
			}
		}

		/// <summary>
		/// Gets or sets the tag.
		/// </summary>
		/// <value>The tag.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public object Tag
		{
			get
			{
				return _Tag;
			}
			set
			{
				if (_Tag != value)
				{
					_Tag = value;
					OnPropertyChanged("Tag");
				}
			}
		}

		/// <summary>
		/// Gets or sets the serializable UID Guid of slide.
		/// </summary>
		/// <value>The serializable UID Guid of slide.</value>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public Uid Uid
		{
			get 
			{
				return _Uid;
			}
			set 
			{
				if (_Uid != value)
				{
					_Uid = value;
					OnPropertyChanged("Guid");
				}
			}
		}
		#endregion


		#region Constructors
		/// <summary>
		/// Initializes a new instance of the <see cref="Slide"/> class.
		/// </summary>
		public Slide()
		{
			_Uid = new Uid();
			//_Size = new Size(1024, 768);
		}
		#endregion


		#region Methods
		/// <summary>
		/// Attaches the canvas handlers.
		/// </summary>
		/// <param name="canvas">The canvas.</param>
		protected void AttachCanvasHandlers(InkCanvas canvas)
		{
			canvas.Loaded += new RoutedEventHandler(Canvas_Loaded);
			canvas.Strokes.StrokesChanged += new StrokeCollectionChangedEventHandler(Strokes_StrokesChanged);
			canvas.SelectionMoved += new EventHandler(Canvas_SelectionMoved);
			canvas.SelectionResized += new EventHandler(Canvas_SelectionResized);
		}

		/// <summary>
		/// Detaches the canvas handlers.
		/// </summary>
		/// <param name="canvas">The canvas.</param>
		protected void DetachCanvasHandlers(InkCanvas canvas)
		{
			canvas.Loaded -= Canvas_Loaded;
			canvas.Strokes.StrokesChanged -= Strokes_StrokesChanged;
			canvas.SelectionMoved -= Canvas_SelectionMoved;
			canvas.SelectionResized -= Canvas_SelectionResized;
		}

		/// <summary>
		/// Handles the Loaded event of the Canvas control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
		protected void Canvas_Loaded(object sender, RoutedEventArgs e)
		{
			OnPropertyChanged("Canvas");
		}

		/// <summary>
		/// Handles the StrokesChanged event of the Strokes control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Windows.Ink.StrokeCollectionChangedEventArgs"/> instance containing the event data.</param>
		protected void Strokes_StrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
		{
			OnPropertyChanged("Canvas");
		}

		/// <summary>
		/// Handles the SelectionMoved event of the Canvas control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		protected void Canvas_SelectionMoved(object sender, EventArgs e)
		{
			OnPropertyChanged("Canvas");
		}

		/// <summary>
		/// Handles the SelectionResized event of the Canvas control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		protected void Canvas_SelectionResized(object sender, EventArgs e)
		{
			OnPropertyChanged("Canvas");
		}

		/// <summary>
		/// Called when some property is changed.
		/// </summary>
		/// <param name="propertyName">Name of the property.</param>
		protected void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}

		/// <summary>
		/// Updates the thumbnail.
		/// </summary>
		public void UpdateThumbnail()
		{
			OnPropertyChanged("Canvas");
		}
		#endregion


		#region ISerializable Members
		/// <summary>
		/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
		/// </summary>
		/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
		/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
		/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception>
		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			throw new System.NotImplementedException();
		}
		#endregion


		#region INotifyPropertyChanged Members
		/// <summary>
		/// Occurs when a property value changes.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;
		#endregion


		#region ICloneable Members
		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>A new object that is a copy of this instance.</returns>
		public object Clone()
		{
			var slide = new Slide();

			slide.Size = this.Size;
			slide.Canvas = new InkCanvas();
			slide.Canvas.Strokes = (StrokeCollection)this.Canvas.Strokes.Clone();
			slide.Canvas.Background = (Brush)this.Canvas.Background.Clone();
			slide.Uid = this.Uid;

			return slide;
		}
		#endregion
	}
}

